package com.chalk.common.pay.wx;

import com.chalk.common.constant.CommonConstants;
import com.chalk.common.util.IPUtil;
import com.chalk.common.util.MD5Util;
import com.chalk.common.util.XMLUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.util.*;

/**
 * @author xiaoqiang
 * @Description 微信支付工具类
 * @date 2019/1/19 10:33
 */
@Component
public class WxPayUtil {
    /* 应用ID（小程序） */
    private static String APP_ID = "wx510681bea95c8b56";
    /* 应用ID（APP） */
    private static String APP_APP_ID = "wx5ffbf6488a4ee0d7";
    /* 商户号 */
    private static String MCH_ID_RURALSTORE = "1533293411";

    /* apikey */
    private static String APIKEY = "FBKJ20190425FBKJ20190425FBKJ2019";

    /* 交易类型JSAPI--公众号支付、NATIVE--原生扫码支付、APP MICROPAY--刷卡支付 */
    private static String TRADE_TYPE = "APP";

    /* 货币类型 */
    private static String FEE_TYPE = "CNY";

    /* 回调地址 */
    private static String WX_NOTIFY_URL = "https://1chalk.com/mall-app/ignoreAuth/wxpay/notifyUrl";
//    private static String WX_NOTIFY_URL = "http://xiaoqiangge.51vip.biz:29429/ignoreAuth/wxpay/notifyUrl";

    private static Logger log = LoggerFactory.getLogger(WxPayUtil.class);

    /**
     * 小程序支付
     * @param packdto
     * @param request
     * @return
     * @throws Exception
     */
    public static SortedMap<Object, Object> getPrePayMap(WxPackageDTO packdto, HttpServletRequest request) throws Exception {
        packdto.setAppid(APP_ID);
        packdto.setMch_id(MCH_ID_RURALSTORE);
        packdto.setNonce_str(createNoncestr());
        packdto.setBody(packdto.getBody());
        packdto.setOut_trade_no(packdto.getOut_trade_no());
        packdto.setTotal_fee(packdto.getTotal_fee());
        packdto.setSpbill_create_ip(IPUtil.getIpAddr(request));
        packdto.setNotify_url(WX_NOTIFY_URL);

        SortedMap map = getParaMap(packdto);
        String sign = createSign(map,APIKEY);
        packdto.setSign(sign);
        map.put("sign", sign);
        String outputStr = getRequestXml(map);
        String restr = httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", outputStr);
        log.info("微信返回结果：" + restr);
        Map remap = XMLUtil.doXMLParse(restr);
        SortedMap prePayMap = new TreeMap();
        prePayMap.put("appId", packdto.getAppid());
        prePayMap.put("nonceStr", createNoncestr());
//        prePayMap.put("package", "Sign=WXPay");//Sign=WXPay
        prePayMap.put("package", "prepay_id="+(String)remap.get("prepay_id"));//Sign=WXPay
        prePayMap.put("signType", "MD5");
        //6、时间戳
        //自1970年1月1日 0点0分0秒以来的秒数。注意：部分系统取到的值为毫秒级，需要转换成秒(10位数字)。
        prePayMap.put("timeStamp", Long.toString(System.currentTimeMillis()/1000));
        //需要签名的6个参数，不能有大写字母存在,必须按以上的名字命名（顺序可能也是）
        String paySign = createSign(prePayMap,APIKEY);
        prePayMap.put("send_url", packdto.getNotify_url());
        prePayMap.put("partnerid", packdto.getMch_id());
        prePayMap.put("prepayid",(String)remap.get("prepay_id"));
        prePayMap.put("trade_type", remap.get("trade_type"));
        prePayMap.put("pay_sign", paySign);
        return prePayMap;
    }

    /**
     * 微信支付
     * @param packdto
     * @param request
     * @return
     * @throws Exception
     */
    public static SortedMap<Object, Object> WxGetPrePayMap(WxPackageDTO packdto, HttpServletRequest request) throws Exception {
        packdto.setAppid(APP_APP_ID);
        packdto.setMch_id(MCH_ID_RURALSTORE);
        packdto.setNonce_str(createNoncestr());
        packdto.setBody(packdto.getBody());
        packdto.setOut_trade_no(packdto.getOut_trade_no());
        packdto.setTotal_fee(packdto.getTotal_fee());
        packdto.setSpbill_create_ip(IPUtil.getIpAddr(request));
        packdto.setNotify_url(WX_NOTIFY_URL);

        SortedMap map = getParaMap(packdto);
        String sign = createSign(map,APIKEY);
        packdto.setSign(sign);
        map.put("sign", sign);
        String outputStr = getRequestXml(map);
        String restr = httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", outputStr);
        log.info("微信返回结果：" + restr);
        Map remap = XMLUtil.doXMLParse(restr);
        SortedMap prePayMap = new TreeMap();
        prePayMap.put("appid", packdto.getAppid());
        prePayMap.put("noncestr", createNoncestr());
        prePayMap.put("package", "Sign=WXPay");//Sign=WXPay
//        prePayMap.put("package", "prepay_id="+(String)remap.get("prepay_id"));//Sign=WXPay
        prePayMap.put("partnerid", packdto.getMch_id());
        prePayMap.put("prepayid",(String)remap.get("prepay_id"));
        //6、时间戳
        //自1970年1月1日 0点0分0秒以来的秒数。注意：部分系统取到的值为毫秒级，需要转换成秒(10位数字)。
        prePayMap.put("timestamp", Long.toString(System.currentTimeMillis()/1000));
        //需要签名的6个参数，不能有大写字母存在,必须按以上的名字命名（顺序可能也是）
        String paySign = createSign(prePayMap,APIKEY);
        prePayMap.put("signtype", "MD5");
        prePayMap.put("send_url", packdto.getNotify_url());
        prePayMap.put("trade_type", remap.get("trade_type"));
        prePayMap.put("pay_sign", paySign);
        return prePayMap;
    }
    /**
     * 随机字符串创建（指定长度）
     * @param length
     * @return
     */
    public static String createNoncestr(int length) {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String res = "";
        for (int i = 0; i < length; i++) {
            Random rd = new Random();
            res = res + chars.indexOf(rd.nextInt(chars.length() - 1));
        }
        return res;
    }
    /**
     * 随机字符串创建（16长度）
     * @param
     * @return
     */
    public static String createNoncestr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String res = "";
        for (int i = 0; i < 16; i++) {
            Random rd = new Random();
            res = res + chars.charAt(rd.nextInt(chars.length() - 1));
        }
        return res;
    }

    /**
     * 签名
     * @param parameters
     * @param apiKey
     * @return
     */
    public static String createSign(SortedMap<Object, Object> parameters, String apiKey)
    {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if ((v != null) && (!"".equals(v)) && (!"sign".equals(k)) && (!"key".equals(k))) {
                sb.append(k + "=" + v + "&");
            }
        }
//        if(null != apiKey && "".equals(apiKey)){
//            sb.append("key=" + apiKey);
//        }
        sb.append("key=" + apiKey);
        log.info("签名字符串：" + sb);
        String sign = MD5Util.MD5Encode(sb.toString(), CommonConstants.UTF8).toUpperCase();
        return sign;
    }

    /**
     * 验签
     * @param characterEncoding
     * @param packageParams
     * @return
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + APIKEY);
        //算出摘要
        String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
        return tenpaySign.equals(mysign);
    }

    /**
     * xml处理
     * @param parameters
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters)
    {
        StringBuffer sb = new StringBuffer();
        String sign = "";
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if ("sign".equalsIgnoreCase(k))
            {
                sign = "<" + k + ">" + v + "</" + k + ">";
            } else if (("attach".equalsIgnoreCase(k)) || ("body".equalsIgnoreCase(k)))
            {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
            else {sb.append("<" + k + ">" + v + "</" + k + ">");}
        }

        sb.append(sign);
        sb.append("</xml>");
        return sb.toString();
    }

    public static String setXML(String return_code, String return_msg)
    {
        return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }

    /**
     * 发送https请求
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr 提交的数据
     * @return 返回微信服务器响应的信息
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            MyX509TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            javax.net.ssl.SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
            log.error("连接超时：{}", ce);
        } catch (Exception e) {
            log.error("https请求异常：{}", e);
        }
        return null;
    }

    /**
     * 将支付参数转为Map
     * @param dto
     * @return
     */
    public static SortedMap<Object, Object> getParaMap(WxPackageDTO dto) {
        SortedMap map = new TreeMap();
        if (StringUtils.isNotBlank(dto.getAppid())) {
            map.put("appid", dto.getAppid());
        }
        if (StringUtils.isNotBlank(dto.getMch_id())) {
            map.put("mch_id", dto.getMch_id());
        }
        if (StringUtils.isNotBlank(dto.getDevice_info())) {
            map.put("device_info", dto.getDevice_info());
        }
        if (StringUtils.isNotBlank(dto.getNonce_str())) {
            map.put("nonce_str", dto.getNonce_str());
        }
        if (StringUtils.isNotBlank(dto.getSign())) {
            map.put("sign", dto.getSign());
        }
        if (StringUtils.isNotBlank(dto.getBody())) {
            map.put("body", dto.getBody());
        }
        if (StringUtils.isNotBlank(dto.getDetail())) {
            map.put("detail", dto.getDetail());
        }
        if (StringUtils.isNotBlank(dto.getAttach())) {
            map.put("attach", dto.getAttach());
        }
        if (StringUtils.isNotBlank(dto.getOut_trade_no())) {
            map.put("out_trade_no", dto.getOut_trade_no());
        }
        if (StringUtils.isNotBlank(dto.getFee_type())) {
            map.put("fee_type", dto.getFee_type());
        }
        if (StringUtils.isNotBlank(dto.getTotal_fee())) {
            map.put("total_fee", dto.getTotal_fee());
        }
        if (StringUtils.isNotBlank(dto.getSpbill_create_ip())) {
            map.put("spbill_create_ip", dto.getSpbill_create_ip());
        }
        if (StringUtils.isNotBlank(dto.getTime_start())) {
            map.put("time_start", dto.getTime_start());
        }
        if (StringUtils.isNotBlank(dto.getTime_expire())) {
            map.put("time_expire", dto.getTime_expire());
        }
        if (StringUtils.isNotBlank(dto.getGoods_tag())) {
            map.put("goods_tag", dto.getGoods_tag());
        }
        if (StringUtils.isNotBlank(dto.getNotify_url())) {
            map.put("notify_url", dto.getNotify_url());
        }
        if (StringUtils.isNotBlank(dto.getTrade_type())) {
            map.put("trade_type", dto.getTrade_type());
        }
        if (StringUtils.isNotBlank(dto.getProduct_id())) {
            map.put("product_id", dto.getProduct_id());
        }
        if (StringUtils.isNotBlank(dto.getOpenid())) {
            map.put("openid", dto.getOpenid());
        }
        return map;
    }

    /**
     * 小程序二次签名
     * @param appId
     * @param partentid
     * @param nonceStr
     * @param prepay_id
     * @return
     */
    public static SortedMap onceSign(String appId,String partentid,String nonceStr,String prepay_id){
        SortedMap prePayMap = new TreeMap();
        prePayMap.put("appId", appId);
        prePayMap.put("noncestr", nonceStr);
        prePayMap.put("package", "prepay_id="+prepay_id);//Sign=WXPay
        //6、时间戳
        //自1970年1月1日 0点0分0秒以来的秒数。注意：部分系统取到的值为毫秒级，需要转换成秒(10位数字)。
        prePayMap.put("timestamp", Long.toString(System.currentTimeMillis()/1000));
        prePayMap.put("sign_type", "MD5");
        //需要签名的6个参数，不能有大写字母存在,必须按以上的名字命名（顺序可能也是）
        String paySign = createSign(prePayMap,APIKEY);
        prePayMap.put("partnerid", partentid);
        prePayMap.put("prepayid",prepay_id);
        prePayMap.put("pay_sign", paySign);
        return prePayMap;
    }
}
